home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTFTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  35.1 KB  |  1,409 lines

  1. /*            File Transfer Protocol (FTP) Client
  2. **            for a WorldWideWeb browser
  3. **            ===================================
  4. **
  5. **    A cache of control connections is kept (NOT ANYMORE -- IT WAS BROKEN)
  6. **
  7. ** Note: Port allocation
  8. **
  9. **    It is essential that the port is allocated by the system, rather
  10. **    than chosen in rotation by us (POLL_PORTS), or the following
  11. **    problem occurs.
  12. **
  13. **    It seems that an attempt by the server to connect to a port which has
  14. **    been used recently by a listen on the same socket, or by another
  15. **    socket this or another process causes a hangup of (almost exactly)
  16. **    one minute. Therefore, we have to use a rotating port number.
  17. **    The problem remains that if the application is run twice in quick
  18. **    succession, it will hang for what remains of a minute.
  19. **
  20. ** Authors
  21. **    TBL    Tim Berners-lee <timbl@info.cern.ch>
  22. **    DD    Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
  23. **    MWM    Mike Meyer
  24. ** History:
  25. **     2 May 91    Written TBL, as a part of the WorldWideWeb project.
  26. **    15 Jan 92    Bug fix: close() was used for NETCLOSE for control soc
  27. **    10 Feb 92    Retry if cached connection times out or breaks
  28. **     8 Dec 92    Bug fix 921208 TBL after DD
  29. **    17 Dec 92    Anon FTP password now just WWWuser@ suggested by DD
  30. **            fails on princeton.edu!
  31. **    18 Oct 93    Allow ftp past firewall sometimes
  32. **
  33. */
  34.  
  35. /* SOCKS mods by:
  36.  * Ying-Da Lee, <ylee@syl.dl.nec.com>
  37.  * NEC Systems Laboratory
  38.  * C&C Software Technology Center
  39.  */
  40.  
  41. #include "HTFTP.h"      /* Implemented here */
  42.  
  43. #define LINE_LENGTH 1024
  44.  
  45. #include "HTParse.h"
  46. #include "HTUtils.h"
  47. #include "tcp.h"
  48. #include "HTTCP.h"
  49. #include "HTAnchor.h"
  50. #include "HTFile.h"
  51. #include "HTChunk.h"
  52. #include "HTSort.h"
  53. #include "HText.h"
  54. #include "HTAlert.h"
  55.  
  56. #ifndef _DNET
  57.  
  58. #ifndef IPPORT_FTP
  59. #define IPPORT_FTP    21
  60. #endif
  61.  
  62. #ifdef __STDC__
  63. #include <stdlib.h>
  64. #endif
  65.  
  66. /* #define TRACE 1 */
  67.  
  68. #ifndef NIL
  69. #define NIL 0
  70. #endif
  71.  
  72. /*        Hypertext object building machinery
  73. */
  74. #include "HTML.h"
  75.  
  76. #define PUTC(c) (*targetClass.put_character)(target, c)
  77. #define PUTS(s) (*targetClass.put_string)(target, s)
  78. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  79. #define END(e) (*targetClass.end_element)(target, e)
  80. #define END_TARGET (*targetClass.end_document)(target)
  81. #define FREE_TARGET (*targetClass.free)(target)
  82. struct _HTStructured {
  83.     CONST HTStructuredClass *    isa;
  84.     /* ... */
  85. };
  86.  
  87. struct _HTStream {
  88.       CONST HTStreamClass*    isa;
  89.       /* ... */
  90. };
  91.  
  92.  
  93. #ifdef SOCKS
  94. extern struct in_addr SOCKS_ftpsrv; /* in HTFTP.c */
  95. #endif
  96.  
  97.  
  98. /*    Module-Wide Variables
  99. **    ---------------------
  100. */
  101. PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
  102. PRIVATE int control = -1;        /* Current connection */
  103. PRIVATE int    data_soc = -1;        /* Socket for data transfer =invalid */
  104.  
  105. PRIVATE int    master_socket = -1;    /* Listening socket = invalid    */
  106. PRIVATE char    port_command[255];    /* Command for setting the port */
  107. PRIVATE BOOL    passive;        /* we have a passive socket */
  108.  
  109.  
  110. #define DATA_BUFFER_SIZE 2048
  111. PRIVATE char data_buffer[DATA_BUFFER_SIZE];        /* Input data buffer */
  112. PRIVATE char * data_read_pointer;
  113. PRIVATE char * data_write_pointer;
  114.  
  115.  
  116. /*    Procedure: Read a character from the data connection
  117. **    ----------------------------------------------------
  118. */
  119. PRIVATE int interrupted_in_next_data_char = 0;
  120. PRIVATE char next_data_char NOARGS
  121. {
  122.   int status;
  123.   interrupted_in_next_data_char = 0;
  124.   if (data_read_pointer >= data_write_pointer)
  125.     {
  126.       status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
  127.       if (status == HT_INTERRUPTED)
  128.     interrupted_in_next_data_char = 1;
  129.       if (status <= 0)
  130.     return (char)-1;
  131.       data_write_pointer = data_buffer + status;
  132.       data_read_pointer = data_buffer;
  133.     }
  134.   return *data_read_pointer++;
  135. }
  136.  
  137.  
  138. /*    Execute Command and get Response
  139. **    --------------------------------
  140. **
  141. **    See the state machine illustrated in RFC959, p57. This implements
  142. **    one command/reply sequence.  It also interprets lines which are to
  143. **    be continued, which are marked with a "-" immediately after the
  144. **    status code.
  145. **
  146. **    Continuation then goes on until a line with a matching reply code
  147. **    an a space after it.
  148. **
  149. ** On entry,
  150. **    con    points to the connection which is established.
  151. **    cmd    points to a command, or is NIL to just get the response.
  152. **
  153. **    The command is terminated with the CRLF pair.
  154. **
  155. ** On exit,
  156. **    returns:  The first digit of the reply type,
  157. **          or negative for communication failure.
  158. */
  159. #ifdef __STDC__
  160. PRIVATE int response (char * cmd)
  161. #else
  162. PRIVATE int response (cmd)
  163.     char * cmd;
  164. #endif
  165. {
  166.   int result;                /* Three-digit decimal code */
  167. #ifdef OLD
  168.   int continuation_response = -1;
  169. #endif
  170.   int status;
  171.   char    continuation;
  172.   int multiline_response = 0;
  173.  
  174.   if (!control ){
  175.       if(TRACE) fprintf(stderr, " BAD ERROR MESSAGE FTP: No control connection set up!!\n");
  176.       if(TRACE) fprintf(stderr, " MJW FTP: actually control == 0 "
  177.             " WHICH IS PERFECTLY OK!!!! "
  178.             " NOT DOING return -99;");
  179.   }
  180.   if ( control == -1)
  181.     {
  182.       if(TRACE)
  183.     fprintf(stderr, "FTP: No control connection set up!!\n");
  184.       return -99;
  185.     }
  186.  
  187.   if (cmd)
  188.     {
  189.       if (TRACE)
  190.     fprintf(stderr, "  Tx: %s", cmd);
  191.  
  192.       status = NETWRITE(control, cmd, (int)strlen(cmd));
  193.       if (status<0)
  194.     {
  195.       if (TRACE) fprintf(stderr,
  196.                  "FTP: Error %d sending command: closing socket %d\n",
  197.                  status, control);
  198.       NETCLOSE(control);
  199.       control = -1;
  200.       return status;
  201.     }
  202.     }
  203.  
  204.   /* Patch to be generally compatible with RFC 959 servers  -spok@cs.cmu.edu  */
  205.   /* Multiline responses start with a number and a hyphen;
  206.      end with same number and a space.    When it ends, the number must
  207.      be flush left. */
  208.   do
  209.     {
  210.       char *p = response_text;
  211.       /* If nonzero, it's set to initial code of multiline response */
  212.       for (;;)
  213.     {
  214.       int foo;
  215.       /* This is set to 0 at the start of HTGetCharacter. */
  216.       extern int interrupted_in_htgetcharacter;
  217.  
  218.       foo = (*p++ = HTGetCharacter ());
  219.       if (interrupted_in_htgetcharacter)
  220.         {
  221.           if (TRACE)
  222.         fprintf (stderr, "FTP: Interrupted in HTGetCharacter, apparently.\n");
  223.           NETCLOSE (control);
  224.           control = -1;
  225.           return HT_INTERRUPTED;
  226.         }
  227.  
  228.       if (foo == LF ||
  229.        /* if (((*p++=NEXT_CHAR) == '\n') || */
  230.           (p == &response_text[LINE_LENGTH]))
  231.         {
  232.           *p++=0;              /* Terminate the string */
  233.           if (TRACE)
  234.         fprintf(stderr, "    Rx: %s", response_text);
  235.           sscanf(response_text, "%d%c", &result, &continuation);
  236.           if (continuation == '-' && !multiline_response)
  237.         {
  238.           multiline_response = result;
  239.         }
  240.           else if (multiline_response && continuation == ' ' &&
  241.                multiline_response == result &&
  242.                isdigit(response_text[0]))
  243.         {
  244.           /* End of response (number must be flush on left) */
  245.           multiline_response = 0;
  246.         }
  247.           break;
  248.         } /* if end of line */
  249.  
  250.       if (*(p-1) < 0)
  251.         {
  252.           if (TRACE)
  253.         fprintf(stderr, "Error on rx: closing socket %d\n",
  254.             control);
  255.           strcpy (response_text, "000 *** TCP read error on response\n");
  256.           NETCLOSE(control);
  257.           control = -1;
  258.           return -1;    /* End of file on response */
  259.         }
  260.     } /* Loop over characters */
  261.     }
  262.   while (multiline_response);
  263.  
  264. #ifdef OLD
  265.   do
  266.     {
  267.       char *p = response_text;
  268.       for(;;)
  269.     {
  270.       int foo;
  271.       /* This is set to 0 at the start of HTGetCharacter. */
  272.       extern int interrupted_in_htgetcharacter;
  273.  
  274.       foo = (*p++ = HTGetCharacter ());
  275.       if (interrupted_in_htgetcharacter)
  276.         {
  277.           if (TRACE)
  278.         fprintf (stderr, "FTP: Interrupted in HTGetCharacter, apparently.\n");
  279.           NETCLOSE (control);
  280.           control = -1;
  281.           return HT_INTERRUPTED;
  282.         }
  283.  
  284.       if (foo == LF ||
  285.           p == &response_text[LINE_LENGTH])
  286.         {
  287.           char continuation;
  288.           int rv;
  289.  
  290.           *p++=0;            /* Terminate the string */
  291.           if (TRACE)
  292.         fprintf(stderr, "    Rx: %s", response_text);
  293.           /* Clear out result ahead of time to see if we couldn't
  294.          read a real value. */
  295.           result = -1;
  296.           rv = sscanf(response_text, "%d%c", &result, &continuation);
  297.           /* Try just continuing if we couldn't pull out
  298.          a value for result and the response_text starts with
  299.          whitespace. */
  300.           if (rv < 2 && result == -1 &&
  301.           (*response_text == ' ' || *response_text == '\t'))
  302.         {
  303.           /* Dunno what to do here -- the code isn't really
  304.              set up to deal with continuation lines starting
  305.              with whitespace.  Testcase is
  306.              reports.adm.cs.cmu.edu. */
  307.         }
  308.           else if (continuation_response == -1)
  309.         {
  310.           if (continuation == '-')  /* start continuation */
  311.             continuation_response = result;
  312.         }
  313.           else
  314.         {    /* continuing */
  315.           if (continuation_response == result && continuation == ' ')
  316.             continuation_response = -1; /* ended */
  317.         }
  318.           break;
  319.         } /* if end of line */
  320.  
  321.       if (*(p-1) == EOF)
  322.         {
  323.           if (TRACE)
  324.         fprintf(stderr, "Error on rx: closing socket %d\n",
  325.             control);
  326.           strcpy (response_text, "000 *** TCP read error on response\n");
  327.           NETCLOSE(control);
  328.           control = -1;
  329.           return -1;    /* End of file on response */
  330.         }
  331.     } /* Loop over characters */
  332.  
  333.     }
  334.   while (continuation_response != -1);
  335. #endif
  336.  
  337.   if (result == 421)
  338.     {
  339.       if(TRACE)
  340.     fprintf(stderr, "FTP: They close so we close socket %d\n",
  341.         control);
  342.       NETCLOSE(control);
  343.       return -1;
  344.     }
  345.   return result/100;
  346. }
  347.  
  348.  
  349. /*    Get a valid connection to the host
  350. **    ----------------------------------
  351. **
  352. ** On entry,
  353. **    arg    points to the name of the host in a hypertext address
  354. ** On exit,
  355. **    returns <0 if error
  356. **        socket number if success
  357. **
  358. **    This routine takes care of managing timed-out connections, and
  359. **    limiting the number of connections in use at any one time.
  360. **
  361. **    It ensures that all connections are logged in if they exist.
  362. **    It ensures they have the port number transferred.
  363. */
  364. PRIVATE int get_connection ARGS1 (char *,arg)
  365. {
  366.   int status, con;
  367.  
  368.   char *username = 0;
  369.   char *password = 0;
  370.   char dummy[MAXHOSTNAMELEN+32];
  371.  
  372.   if (!arg)
  373.     return -1;        /* Bad if no name sepcified    */
  374.   if (!*arg)
  375.     return -1;        /* Bad if name had zero length    */
  376.  
  377.   if (TRACE)
  378.     fprintf(stderr, "FTP: Looking for %s\n", arg);
  379.  
  380.   {
  381.     char *p1 = HTParse(arg, "", PARSE_HOST);
  382.     char *p2 = strrchr(p1, '@');        /* user? */
  383.     char * pw;
  384.     if (p2)
  385.       {
  386.     username = p1;
  387.     *p2=0;                  /* terminate */
  388.     p1 = p2+1;              /* point to host */
  389.     pw = strchr(username, ':');
  390.     if (pw)
  391.       {
  392.         *pw++ = 0;
  393.         password = pw;
  394.       }
  395.       }
  396.  
  397.     /* copy hostname into dummy URL, since username:password@
  398.        might have been part of original */
  399.     sprintf(dummy, "ftp://%s", p1);
  400.  
  401.     if (!username)
  402.       free(p1);
  403.   }
  404.  
  405.   con = -1;
  406.   status = HTDoConnect (dummy, "FTP", IPPORT_FTP, &con);
  407.  
  408.   if (status < 0)
  409.     {
  410.       if (TRACE)
  411.     {
  412.       if (status == HT_INTERRUPTED)
  413.         fprintf (stderr,
  414.              "FTP: Interrupted on connect\n");
  415.       else
  416.         fprintf(stderr,
  417.             "FTP: Unable to connect to remote host for `%s'.\n",
  418.             arg);
  419.     }
  420.       if (status == HT_INTERRUPTED)
  421.     HTProgress ("Connection interrupted.");
  422.       if (con != -1)
  423.     {
  424.       NETCLOSE(con);
  425.       con = -1;
  426.     }
  427.       if (username)
  428.     free(username);
  429.       HTProgress ("Unable to connect to remote host.");
  430.       return status;            /* Bad return */
  431.     }
  432.  
  433.   if (TRACE)
  434.     fprintf(stderr, "FTP connected, assigning control socket %d\n", con);
  435.   control = con;            /* Current control connection */
  436.  
  437.   /* Initialise buffering for contron connection */
  438.   HTInitInput (con);
  439.  
  440.   /* Now we log in; Look up username, prompt for pw. */
  441.   {
  442.     int status = response (NIL);    /* Get greeting */
  443.  
  444.     if (status == HT_INTERRUPTED)
  445.       {
  446.     if (TRACE)
  447.       fprintf (stderr,
  448.            "FTP: Interrupted at beginning of login.\n");
  449.     HTProgress ("Connection interrupted.");
  450.     NETCLOSE(control);
  451.     control = -1;
  452.     return HT_INTERRUPTED;
  453.       }
  454.     if (status == 2)
  455.       {     /* Send username */
  456.     char * command;
  457.     if (username)
  458.       {
  459.         command = (char*)malloc(10+strlen(username)+2+1);
  460.         sprintf(command, "USER %s%c%c", username, CR, LF);
  461.       }
  462.     else
  463.       {
  464.         command = (char*)malloc(25);
  465.         sprintf(command, "USER anonymous%c%c", CR, LF);
  466.       }
  467.     status = response (command);
  468.     free(command);
  469.     if (status == HT_INTERRUPTED)
  470.       {
  471.         if (TRACE)
  472.           fprintf (stderr,
  473.                "FTP: Interrupted while sending username.\n");
  474.         HTProgress ("Connection interrupted.");
  475.         NETCLOSE(control);
  476.         control = -1;
  477.         return HT_INTERRUPTED;
  478.       }
  479.       }
  480.     if (status == 3)
  481.       {     /* Send password */
  482.     char * command;
  483.     if (password)
  484.       {
  485.         command = (char*)malloc(10+strlen(password)+2+1);
  486.         sprintf(command, "PASS %s%c%c", password, CR, LF);
  487.       }
  488.     else
  489.       {
  490.         char * user = getenv("USER");
  491.         extern char *machine_with_domain;
  492.         char *host = machine_with_domain;
  493.         if (!user)
  494.           user = "WWWuser";
  495.         /* If not fully qualified, suppress it as ftp.uu.net
  496.            prefers a blank to a bad name */ /* check for null first MJW*/
  497.         if (!host || !strchr(host, '.')) host = "";
  498.  
  499.         command = (char*)malloc(20+strlen(host)+2+1);
  500.         sprintf(command,
  501.             "PASS %s@%s%c%c", user ? user : "WWWuser",
  502.             host, CR, LF); /*@@*/
  503.       }
  504.     status = response (command);
  505.     free(command);
  506.     if (status == HT_INTERRUPTED)
  507.       {
  508.         if (TRACE)
  509.           fprintf (stderr,
  510.                "FTP: Interrupted while sending password.\n");
  511.         HTProgress ("Connection interrupted.");
  512.         NETCLOSE(control);
  513.         control = -1;
  514.         return HT_INTERRUPTED;
  515.       }
  516.       }
  517.     if (username)
  518.       free(username);
  519.  
  520.     if (status == 3)
  521.       {
  522.     char temp[80];
  523.     sprintf (temp, "ACCT noaccount%c%c", CR, LF);
  524.     status = response (temp);
  525.     if (status == HT_INTERRUPTED)
  526.       {
  527.         if (TRACE)
  528.           fprintf (stderr,
  529.                "FTP: Interrupted while sending ACCT.\n");
  530.         HTProgress ("Connection interrupted.");
  531.         NETCLOSE(control);
  532.         control = -1;
  533.         return HT_INTERRUPTED;
  534.       }
  535.       }
  536.     if (status != 2)
  537.       {
  538.     if (TRACE)
  539.       fprintf(stderr, "FTP: Login fail: %s", response_text);
  540.     NETCLOSE(control);
  541.     control = -1;
  542.     return -1;        /* Bad return */
  543.       }
  544.     if (TRACE)
  545.       fprintf(stderr, "FTP: Logged in.\n");
  546.   }
  547.  
  548.   return con;            /* Good return */
  549. } /* Scope of con */
  550.  
  551.  
  552.  
  553. /*    Close Master (listening) socket
  554. **    -------------------------------
  555. **
  556. **
  557. */
  558. #ifdef __STDC__
  559. PRIVATE void close_master_socket(void)
  560. #else
  561. PRIVATE void close_master_socket()
  562. #endif
  563. {
  564.   if (TRACE)
  565.     fprintf(stderr, "FTP: Closing master socket %d\n", master_socket);
  566.   if (master_socket >= 0) NETCLOSE(master_socket);
  567.   master_socket = -1;
  568.  
  569.   return;
  570. }
  571.  
  572.  
  573.   /*    MWM   (mwm@contessa.phone.net) FIREWALL Code
  574.    **     THE END OF THIS SECTION IS EXPLICITLY MARKED BELOW
  575.   **    Open a passive master socket for data
  576.   **    -------------------------------------
  577.   **
  578.   **    With the server in passive mode, we connect to the server to get the
  579.   **    data, rather than waiting for it to send us the data. This allows
  580.   **    FTP to work through a firewall that allows outgoing but not incoming
  581.   **    TCP connections.
  582.   **
  583.   ** On entry,
  584.   **    master_socket    Must be negative if not set up already.
  585.   ** On exit,
  586.   **    Returns     socket number if good,
  587.   **            less than zero if error.
  588.   **    data_soc    is socket number if good, else negative.
  589.   **    passive     is TRUE if remote server is passive, else FALSE.
  590.   */
  591.   #ifdef __STDC__
  592.   PRIVATE int get_passive_socket(void)
  593.   #else
  594.   PRIVATE int get_passive_socket()
  595.   #endif
  596.   {
  597.       int status;
  598.       struct sockaddr_in soc_address;        /* Binary network address */
  599.       char pasv_command[6];
  600.  
  601.       passive = FALSE;
  602.       sprintf(pasv_command, "PASV%c%c", CR, LF);
  603.       status = response(pasv_command);
  604.       CTRACE(stderr, "FTP: PASV command returned %d\n", status);
  605.       if (2 != status) return -status ;
  606.  
  607.   /*    The other end sent us a port number; get the host and port number
  608.   */
  609.       {
  610.     char host_addr[16];
  611.     int host_port;
  612.     char *resp;
  613.     int h1, h2, h3, h4;    /* Host number */
  614.     int p1, p2;        /* Port number */
  615.  
  616.     resp = index(response_text, '(');
  617.     if (!resp) {    /* Sigh */
  618.         resp = response_text + strlen(response_text) - 1 ;
  619.         /* Back over any trailing whitespace */
  620.         while (resp > response_text && isspace(*resp))
  621.         resp -= 1 ;
  622.         /* Now, back over the port numbers */
  623.         while (resp > response_text && !isspace(*resp))
  624.         resp -= 1 ;
  625.     }
  626.     sscanf(resp + 1, "%d,%d,%d,%d,%d,%d", &h1, &h2, &h3, &h4, &p1, &p2);
  627.     host_port = (p1 << 8) + p2;
  628.     sprintf(host_addr, "%d.%d.%d.%d", h1, h2, h3, h4);
  629.     CTRACE(stderr, "FTP: Passive port at %d, IP address %s\n",
  630.         host_port, host_addr);
  631.  
  632.     soc_address.sin_family = AF_INET;
  633.     soc_address.sin_port = htons(host_port);
  634.     soc_address.sin_addr.s_addr = inet_addr(host_addr);
  635.       }
  636.  
  637.   /*    let's get a socket set up from the server.
  638.   */
  639.       {
  640.     int new_socket;
  641.  
  642.     status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  643.     if (status < 0) {
  644.         HTInetStatus("socket");
  645.         return status;
  646.     }
  647.     new_socket = status;
  648.  
  649.     status = connect(new_socket, (struct sockaddr *)&soc_address,
  650.         sizeof(soc_address));
  651.     if (status < 0) {
  652.         HTInetStatus("connect");
  653.         CTRACE(stderr,
  654.         "FTP: Unable to connect to remote host via passive socket.\n");
  655.         NETCLOSE(new_socket);
  656.         return status;        /* Bad return! Bad! Bad! */
  657.     }
  658.     CTRACE(stderr, "FTP: connected to pasive datasocket %d\n", new_socket);
  659.  
  660.     if (data_soc >= 0) {
  661.         NETCLOSE(data_soc);
  662.         CTRACE(stderr, "FTP: Closing data socket %d\n", data_soc);
  663.     }
  664.     data_soc = new_socket;
  665.     passive = TRUE;
  666.     return new_socket;
  667.       }
  668.   }    /* get_data_socket */
  669.  
  670. /* END OF MWM (mwm@contessa.phone.net) FIREWALL CODE */
  671.  
  672. /*    Open a master socket for listening on
  673. **    -------------------------------------
  674. **
  675. **    When data is transferred, we open a port, and wait for the server to
  676. **    connect with the data.
  677. **
  678. ** On entry,
  679. **    master_socket    Must be negative if not set up already.
  680. ** On exit,
  681. **    Returns     socket number if good
  682. **            less than zero if error.
  683. **    master_socket    is socket number if good, else negative.
  684. **    port_number    is valid if good.
  685. */
  686. #ifdef __STDC__
  687. PRIVATE int get_listen_socket(void)
  688. #else
  689. PRIVATE int get_listen_socket()
  690. #endif
  691. {
  692.   struct sockaddr_in soc_address;    /* Binary network address */
  693.   struct sockaddr_in *sin = &soc_address;
  694.   int new_socket;            /* Will be master_socket */
  695.  
  696.   /* Create internet socket */
  697.   new_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
  698.  
  699.   if (new_socket < 0)
  700.     return -1;
  701.  
  702.   if (TRACE)
  703.     fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
  704.  
  705.   /* Search for a free port. */
  706.   sin->sin_family = AF_INET;        /* Family = internet, host order  */
  707.   sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
  708.   {
  709.     int status;
  710.     int address_length = sizeof(soc_address);
  711. #ifdef SOCKS
  712.     status = Rgetsockname(control,
  713. #else
  714.     status = getsockname(control,
  715. #endif
  716.              (struct sockaddr *)&soc_address,
  717.              &address_length);
  718.     if (status<0)
  719.       return -1;
  720.     CTRACE(tfp, "FTP: This host is %s\n",
  721.        HTInetString(sin));
  722.  
  723.     soc_address.sin_port = 0; /* Unspecified: please allocate */
  724. #ifdef SOCKS
  725.     status=Rbind(new_socket,
  726. #else
  727.     status=bind(new_socket,
  728. #endif
  729.           (struct sockaddr*)&soc_address,
  730.         /* Cast to generic sockaddr */
  731. #ifdef SOCKS
  732.           sizeof(soc_address), SOCKS_ftpsrv.s_addr);
  733. #else
  734.           sizeof(soc_address));
  735. #endif
  736.       if (status<0)
  737.     return -1;
  738.  
  739.       address_length = sizeof(soc_address);
  740. #ifdef SOCKS
  741.     status = Rgetsockname(new_socket,
  742. #else
  743.     status = getsockname(new_socket,
  744. #endif
  745.                (struct sockaddr*)&soc_address,
  746.                &address_length);
  747.     if (status<0)
  748.       return -1;
  749.   }
  750.  
  751.   CTRACE(tfp, "FTP: bound to port %d on %s\n",
  752.      (int)ntohs(sin->sin_port),
  753.      HTInetString(sin));
  754.  
  755.   if (master_socket >= 0)
  756.     close_master_socket ();
  757.  
  758.   master_socket = new_socket;
  759.  
  760.   /* Now we must find out who we are to tell the other guy */
  761.   (void)HTHostName();    /* Make address valid - doesn't work*/
  762.   sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
  763.       (int)*((unsigned char *)(&sin->sin_addr)+0),
  764.       (int)*((unsigned char *)(&sin->sin_addr)+1),
  765.       (int)*((unsigned char *)(&sin->sin_addr)+2),
  766.       (int)*((unsigned char *)(&sin->sin_addr)+3),
  767.       (int)*((unsigned char *)(&sin->sin_port)+0),
  768.       (int)*((unsigned char *)(&sin->sin_port)+1),
  769.       CR, LF);
  770.  
  771.   /* Inform TCP that we will accept connections */
  772. #ifdef SOCKS
  773.   if (Rlisten (master_socket, 1) < 0)
  774. #else
  775.   if (listen (master_socket, 1) < 0)
  776. #endif
  777.     {
  778.       close_master_socket ();
  779.       return -1;
  780.     }
  781.   CTRACE(tfp, "FTP: Master socket(), bind() and listen() all OK\n");
  782.  
  783.   return master_socket;     /* Good */
  784. } /* get_listen_socket */
  785.  
  786.  
  787.  
  788. /*    Read a directory into an hypertext object from the data socket
  789. **    --------------------------------------------------------------
  790. **
  791. ** On entry,
  792. **    anchor        Parent anchor to link the this node to
  793. **    address     Address of the directory
  794. ** On exit,
  795. **    returns     HT_LOADED if OK
  796. **            <0 if error.
  797. **
  798. ** Author: Charles Henrich (henrich@crh.cl.msu.edu)  October 2, 1993
  799. **
  800. ** Completely re-wrote this chunk of code to present FTP directory information
  801. ** in a much more useful manner.  Also included the use of icons. -Crh
  802. */
  803. PRIVATE int read_directory
  804. ARGS4 (
  805.   HTParentAnchor *,        parent,
  806.   CONST char *,         address,
  807.   HTFormat,            format_out,
  808.   HTStream *,            sink )
  809. {
  810.   HText *HT = HText_new ();
  811.   HTFormat format;
  812.   HTAtom *pencoding;
  813.   char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
  814.   char buffer[BUFSIZ];
  815.   char itemtype;
  816.   char itemname[BUFSIZ];
  817.   char itemsize[BUFSIZ];
  818.   char *full_ftp_name, *ptr;
  819.   int count, ret, cmpr, c, rv;
  820.   extern char *HTgeticonname(HTFormat, char *);
  821.  
  822.   HText_beginAppend (HT);
  823.  
  824.   HText_appendText (HT, "<H1>FTP Directory ");
  825.   HText_appendText (HT, filename);
  826.   HText_appendText (HT, "</H1>\n");
  827.   HText_appendText (HT, "<DL>\n");
  828.   data_read_pointer = data_write_pointer = data_buffer;
  829.  
  830.   /* If this isnt the root level, spit out a parent directory entry */
  831.  
  832.   if(strcmp(filename,"/") != 0)
  833.     {
  834.       HText_appendText(HT,"<DD>");
  835.  
  836.       HText_appendText(HT,"<A HREF=\"");
  837.  
  838.       strcpy(buffer,filename);
  839.       ptr = strrchr(buffer,'/');
  840.  
  841.       if(ptr != NULL) *ptr='\0';
  842.  
  843.       if(buffer[0] == '\0')
  844.     HText_appendText(HT,"/");
  845.       else
  846.     HText_appendText(HT, buffer);
  847.  
  848.       HText_appendText(HT,"\"><IMG SRC=\"");
  849.       HText_appendText(HT, HTgeticonname(NULL, "directory"));
  850.       HText_appendText(HT,"\"> Parent Directory</a>");
  851.     }
  852.  
  853.   /* Loop until we hit EOF */
  854.   while(1)
  855.     {
  856.       /* Read in a line of data */
  857.       for(count = 0; count < BUFSIZ; count++)
  858.     {
  859.       c = next_data_char ();
  860.       if (interrupted_in_next_data_char)
  861.         {
  862.           if (TRACE)
  863.         fprintf (stderr, "FTP: Picked up interrupted_in_next_data_char\n");
  864.           return HT_INTERRUPTED;
  865.         }
  866.  
  867.       if (c == '\r')
  868.         {
  869.           c = next_data_char ();
  870.           if (interrupted_in_next_data_char)
  871.         {
  872.           if (TRACE)
  873.             fprintf
  874.               (stderr, "FTP: Picked up interrupted_in_next_data_char\n");
  875.           return HT_INTERRUPTED;
  876.         }
  877.  
  878.           if (c != '\n')
  879.         break;
  880.         }
  881.  
  882.       if (c == '\n' || c == (char)EOF)
  883.         break;
  884.  
  885.       buffer[count] = c;
  886.     }
  887.  
  888.       if(c == (char)EOF)
  889.     break;
  890.  
  891.       buffer[count] = 0;
  892.  
  893.       /* Parse the input buffer, extract the item type, and the item size */
  894.  
  895. #if 0
  896.       ret=sscanf(buffer,"%c%*9s%*d %*s %*s %s", &itemtype, itemsize);
  897.  
  898.       if (ret != 2)
  899.     continue;
  900. #endif
  901.       /* Retain whole string -- we don't use it at the moment, but we will. */
  902.       full_ftp_name = strdup (buffer);
  903.       /* Read but disregard itemsize -- this effectively guarantees we will know
  904.      what we should display and what we shouldn't -- don't ask. */
  905.       ret=sscanf(buffer,"%c%*9s%*d %*s %*s %s", &itemtype, itemsize);
  906.  
  907.       if (ret != 2)
  908.     {
  909.       free (full_ftp_name);
  910.       continue;
  911.     }
  912.  
  913.       /* Due to the various time stamp formats, its "safer" to retrieve the        */
  914.       /* filename by taking it from the right side of the string, we do that here. */
  915.       ptr = strrchr(buffer,' ');
  916.  
  917.       if(ptr == NULL)
  918.     continue;
  919.  
  920.       strcpy(itemname,ptr+1);
  921.  
  922.       HText_appendText (HT, "<DD>");
  923.       /* Spit out the anchor refrence, and continue on... */
  924.  
  925.       HText_appendText (HT, "<A HREF=\"");
  926.       /* Assuming it's a relative reference... */
  927.       if (itemname && itemname[0] != '/')
  928.     {
  929.       HText_appendText (HT, filename);
  930.       if (filename[strlen(filename)-1] != '/')
  931.         HText_appendText (HT, "/");
  932.     }
  933.       HText_appendText (HT, itemname);
  934.       HText_appendText (HT, "\"> ");
  935.  
  936.       /* There are 3 "types", directory, link and file.  If its a directory we     */
  937.       /* just spit out the name with a directory icon.    If its a link, we go       */
  938.       /* retrieve the proper name (i.e. the input looks like bob -> ../../../bob   */
  939.       /* so we want to hop past the -> and just grab bob.  The link case falls       */
  940.       /* through to the filetype case.    The filetype shows name and filesize, and  */
  941.       /* then attempts to select the correct icon based on file extension.       */
  942.       switch(itemtype)
  943.     {
  944.     case 'd':
  945.       {
  946.         sprintf(buffer,"%s",itemname);
  947.         HText_appendText(HT, "<IMG SRC=\"");
  948.         HText_appendText(HT, HTgeticonname(NULL, "directory"));
  949.         HText_appendText(HT, "\"> ");
  950.         break;
  951.       }
  952.  
  953.     case 'l':
  954.       {
  955.         ptr = strrchr(buffer,' ');
  956.         if(ptr != NULL)
  957.           {
  958.         *ptr = '\0';
  959.         ptr = strrchr(buffer,' ');
  960.           }
  961.  
  962.         if(ptr != NULL)
  963.           {
  964.         *ptr = '\0';
  965.         ptr = strrchr(buffer,' ');
  966.           }
  967.  
  968.         if(ptr != NULL) strcpy(itemname,ptr+1);
  969.       }
  970.  
  971.     case '-':
  972.       {
  973.  
  974.         /* If this is a link type, and the bytes are small,
  975.            its probably a directory so lets not show the byte
  976.            count */
  977. #if 0
  978.         if(itemtype == 'l' && atoi(itemsize) < 128)
  979.           {
  980.         sprintf(buffer,"%s",itemname);
  981.           }
  982.         else
  983.           {
  984.         sprintf(buffer,"%s (%s bytes)",itemname,itemsize);
  985.           }
  986. #endif
  987.  
  988. #if 0
  989.         if(itemtype == 'l')
  990.           {
  991. #endif
  992.         sprintf(buffer,"%s",itemname);
  993. #if 0
  994.           }
  995.         else
  996.           {
  997.         /* code doesn't work for this, and neither does pre. */
  998.         sprintf(buffer,"<code>%s</code>",full_ftp_name);
  999.           }
  1000. #endif
  1001.  
  1002.         format = HTFileFormat(itemname, &pencoding, WWW_SOURCE, &cmpr);
  1003.  
  1004.         if (1)
  1005.           {
  1006.         HText_appendText(HT, "<IMG SRC=\"");
  1007.  
  1008.         /* If this is a link, and we can't figure out what
  1009.            kind of file it is by extension, throw up the unknown
  1010.            icon; however, if it isn't a link and we can't figure
  1011.            out what it is, throw up the text icon...
  1012.  
  1013.            Unless it's compressed. */
  1014.         if(itemtype == 'l' && cmpr == COMPRESSED_NOT)
  1015.           {
  1016.             /* If it's unknown, let's call it a menu (since symlinks
  1017.                are most commonly used on FTP servers to point to
  1018.                directories, IMHO... -marc */
  1019.             HText_appendText(HT, HTgeticonname(format, "directory") );
  1020.           }
  1021.         else
  1022.           {
  1023.             HText_appendText(HT, HTgeticonname(format, "text"));
  1024.           }
  1025.  
  1026.         HText_appendText(HT, "\"> ");
  1027.           }
  1028.         else
  1029.           {
  1030.         HText_appendText(HT, "<IMG SRC=\"");
  1031.         HText_appendText(HT, HTgeticonname(format, "application"));
  1032.         HText_appendText(HT, "\"> ");
  1033.           }
  1034.  
  1035.         break;
  1036.         }
  1037.  
  1038.       default:
  1039.         {
  1040.           HText_appendText(HT, "<IMG SRC=\"");
  1041.           HText_appendText(HT, HTgeticonname(NULL, "unknown"));
  1042.           HText_appendText(HT, "\"> ");
  1043.           break;
  1044.         }
  1045.       }
  1046.  
  1047.       HText_appendText (HT, buffer);
  1048.       HText_appendText (HT, "</A>\n");
  1049.  
  1050.       free (full_ftp_name);
  1051.     }
  1052.  
  1053.   HText_appendText (HT, "</DL>\n");
  1054. #endif
  1055.   HText_endAppend (HT);
  1056.  
  1057.   rv = response (NIL);
  1058.   if (rv == HT_INTERRUPTED)
  1059.     return rv;
  1060.   return rv == 2 ? HT_LOADED : -1;
  1061. }
  1062.  
  1063.  
  1064. /*    Retrieve File from Server
  1065. **    -------------------------
  1066. **
  1067. ** On entry,
  1068. **    name        WWW address of a file: document, including hostname
  1069. ** On exit,
  1070. **    returns     Socket number for file if good.
  1071. **            <0 if bad.
  1072. */
  1073. #ifndef _DNET
  1074. PUBLIC int HTFTPLoad
  1075. ARGS4 (
  1076.   char *,            name,
  1077.   HTParentAnchor *,        anchor,
  1078.   HTFormat,            format_out,
  1079.   HTStream *,            sink
  1080. )
  1081. {
  1082.   BOOL isDirectory = NO;
  1083.   int status;
  1084.   int retry;            /* How many times tried? */
  1085.   HTFormat format;
  1086.   int compressed = 0;
  1087.  
  1088.   for (retry = 0; retry < 2; retry++)
  1089.     {
  1090.       if (TRACE)
  1091.     fprintf (stderr, "FTP: TRYING in HTFTPLoad, attempt %d\n", retry);
  1092.       status = get_connection(name);
  1093.       if (status < 0)
  1094.     {
  1095.       NETCLOSE (control);
  1096.       control = -1;
  1097.       /* HT_INTERRUPTED will fall through. */
  1098.       return status;
  1099.     }
  1100.  
  1101.        /* MORE MWM PASSIVE SUPPORT - Nov 4 1993 */
  1102.       status = get_passive_socket();
  1103.       if (status < 0)
  1104.        /* END MWM PASSIVE SUPPORT  */
  1105.        status = get_listen_socket();
  1106.       if (status < 0)
  1107.     {
  1108.       NETCLOSE (control);
  1109.       control = -1;
  1110.       close_master_socket ();
  1111.       /* HT_INTERRUPTED would fall through, if we could interrupt
  1112.          somehow in the middle of it, which we currently can't. */
  1113.       return status;
  1114.     }
  1115.  
  1116.       /* Inform the server of the port number we will listen on */
  1117.       if (!passive)
  1118.       {
  1119.     status = response (port_command);
  1120.     if (status == HT_INTERRUPTED)
  1121.       {
  1122.         if (TRACE)
  1123.           fprintf (stderr, "FTP: Interrupted in response (port_command)\n");
  1124.         HTProgress ("Connection interrupted.");
  1125.         NETCLOSE (control);
  1126.         control = -1;
  1127.         close_master_socket ();
  1128.         return HT_INTERRUPTED;
  1129.       }
  1130.     if (status !=2)
  1131.       {        /* Could have timed out */
  1132.         if (status < 0)
  1133.           {
  1134.         NETCLOSE (control);
  1135.         control = -1;
  1136.         close_master_socket ();
  1137.         continue;        /* try again - net error*/
  1138.           }
  1139.  
  1140.         NETCLOSE (control);
  1141.         control = -1;
  1142.         close_master_socket ();
  1143.         return HT_NOT_LOADED;            /* bad reply */
  1144.       }
  1145.     if (TRACE)
  1146.       fprintf(stderr, "FTP: Port defined.\n");
  1147.       }
  1148.       status = 0;
  1149.       break;    /* No more retries */
  1150.     } /* for retries */
  1151.  
  1152.   if (status < 0)
  1153.     {
  1154.       close_master_socket ();
  1155.       NETCLOSE (control);
  1156.       control = -1;
  1157.       return status;    /* Failed with this code */
  1158.     }
  1159.  
  1160.   /* Ask for the file: */
  1161.   {
  1162.     char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
  1163.     char command[LINE_LENGTH+1];
  1164.     HTAtom *encoding;
  1165.  
  1166.     if (!(*filename))
  1167.       StrAllocCopy(filename, "/");
  1168.     format = HTFileFormat (filename, &encoding, WWW_PLAINTEXT, &compressed);
  1169.  
  1170.     sprintf(command, "TYPE %s%c%c", "I", CR, LF);
  1171.     status = response (command);
  1172.     if (status != 2)
  1173.       {
  1174.     if (status == HT_INTERRUPTED)
  1175.       HTProgress ("Connection interrupted.");
  1176.     close_master_socket ();
  1177.     NETCLOSE (control);
  1178.     control = -1;
  1179.     free (filename);
  1180.     return (status == HT_INTERRUPTED) ? HT_INTERRUPTED : -1;
  1181.       }
  1182.  
  1183.     sprintf(command, "RETR %s%c%c", filename, CR, LF);
  1184.     status = response (command);
  1185.     if (status == HT_INTERRUPTED)
  1186.       {
  1187.     if (TRACE)
  1188.       fprintf (stderr, "FTP: Interrupted while sending RETR\n");
  1189.     HTProgress ("Connection interrupted.");
  1190.     NETCLOSE (control);
  1191.     control = -1;
  1192.     close_master_socket ();
  1193.     free (filename);
  1194.     return HT_INTERRUPTED;
  1195.       }
  1196.     if (status != 1)
  1197.       {  /* Failed : try to CWD to it */
  1198.     sprintf(command, "CWD %s%c%c", filename, CR, LF);
  1199.     status = response (command);
  1200.     if (status == HT_INTERRUPTED)
  1201.       {
  1202.         if (TRACE)
  1203.           fprintf (stderr, "FTP: Interrupted while sending CWD\n");
  1204.         HTProgress ("Connection interrupted.");
  1205.         NETCLOSE (control);
  1206.         control = -1;
  1207.         close_master_socket ();
  1208.         free (filename);
  1209.         return HT_INTERRUPTED;
  1210.       }
  1211.     /* Now pick up status == 5 for reports.adm.cs.cmu.edu, which says:
  1212.  
  1213.        Tx: CWD /
  1214.        Rx: 530-Access not allowed for guest users for path "/".
  1215.        Rx:       Here is a hint... If you know the full name of the
  1216.        Rx:       path or directory that you want, try to cd there in
  1217.        Rx:       one step rather than taking little steps in between.
  1218.        Rx:       Those intermediate directories are sometimes protected.
  1219.        Rx:       Or perhaps you are already in the appropriate directory.
  1220.        Rx: 530 Use the pwd command to get the current directory.
  1221.  
  1222.        This may break something.  I dunno...
  1223.     if (status == 2 || status == 5)
  1224.        Yep it broke things... */
  1225.     if (status == 2)
  1226.       {
  1227.         /* Successed : let's NAME LIST it */
  1228.         isDirectory = YES;
  1229.         sprintf(command, "LIST%c%c", CR, LF);
  1230.         status = response (command);
  1231.         if (status == HT_INTERRUPTED)
  1232.           {
  1233.         if (TRACE)
  1234.           fprintf (stderr, "FTP: Interrupted while sending LIST\n");
  1235.         HTProgress ("Connection interrupted.");
  1236.         NETCLOSE (control);
  1237.         control = -1;
  1238.         close_master_socket ();
  1239.         free (filename);
  1240.         return HT_INTERRUPTED;
  1241.           }
  1242.       }
  1243.       }
  1244.     free(filename);
  1245.     if (status != 1)
  1246.       {
  1247.     NETCLOSE (control);
  1248.     control = -1;
  1249.     close_master_socket ();
  1250.     return HT_NOT_LOADED; /* Action not started */
  1251.       }
  1252.   }
  1253.  
  1254.   /* Wait for the connection */
  1255.   if (!passive)
  1256.   {
  1257.     struct sockaddr_in soc_address;
  1258.  
  1259.     int soc_addrlen = sizeof(soc_address);
  1260. #ifdef SOCKS
  1261.     status = Raccept(master_socket,
  1262. #else
  1263.     status = accept(master_socket,
  1264. #endif
  1265.             (struct sockaddr *)&soc_address,
  1266.             &soc_addrlen);
  1267.  
  1268.     if (status < 0)
  1269.       {
  1270.     NETCLOSE (control);
  1271.     control = -1;
  1272.     close_master_socket ();
  1273.     /* We can't interrupt out of an accept. */
  1274.     return HT_NOT_LOADED;
  1275.       }
  1276.  
  1277.     CTRACE(tfp, "FTP: Accepted new socket %d\n", status);
  1278.     data_soc = status;
  1279.   }
  1280.  
  1281.   if (isDirectory)
  1282.     {
  1283.       int s = read_directory (anchor, name, format_out, sink);
  1284.  
  1285.       NETCLOSE (control);
  1286.       control = -1;
  1287.       close_master_socket ();
  1288.       NETCLOSE (data_soc);
  1289.       data_soc = -1;
  1290.  
  1291.       if (TRACE)
  1292.     fprintf (stderr, "FTP: Returning %d after doing read_directory\n", s);
  1293.       /* HT_INTERRUPTED should fall right through. */
  1294.       return s;
  1295.     }
  1296.   else
  1297.     {
  1298.       /* We reproduce ParseSocket below because of socket/child process
  1299.      problem. */
  1300.       HTStream * stream;
  1301.       HTStreamClass targetClass;
  1302.       int rv;
  1303.  
  1304.       stream = HTStreamStack(format,
  1305.                  format_out,
  1306.                  compressed,
  1307.                  sink, anchor);
  1308.  
  1309.       if (!stream)
  1310.     {
  1311.       char buffer[1024];    /* @@@@@@@@ */
  1312.       sprintf(buffer, "Sorry, can't convert from %s to %s.",
  1313.           HTAtom_name(format), HTAtom_name(format_out));
  1314.       HTProgress (buffer);
  1315.       if (TRACE)
  1316.         fprintf(stderr, "FTP: %s\n", buffer);
  1317.       return HT_NOT_LOADED;
  1318.     }
  1319.  
  1320.       targetClass = *(stream->isa);    /* Copy pointers to procedures */
  1321.       rv = HTCopy(data_soc, stream, 0);
  1322.       if (rv == -1)
  1323.     {
  1324.       rv = HT_INTERRUPTED;
  1325.     }
  1326.       else
  1327.     {
  1328.       (*targetClass.end_document)(stream);
  1329.       /* Do NOT call *targetClass.free yet -- sockets aren't closed. */
  1330.       rv = HT_LOADED;
  1331.     }
  1332.  
  1333.       if (TRACE)
  1334.     fprintf (stderr, "FTP: Got back %d from our local equivalent of ParseSocket\n", rv);
  1335.  
  1336.       /* Reset buffering to control connection -- probably
  1337.      no longer necessary, since we don't use a connection
  1338.      more than once. */
  1339.       HTInitInput(control);
  1340.  
  1341.       if (TRACE)
  1342.     fprintf (stderr, "FTP: Closing data socket %d\n", data_soc);
  1343.       NETCLOSE (data_soc);
  1344.       data_soc = -1;
  1345.  
  1346.       /* Unfortunately, picking up the final reply sometimes causes
  1347.      serious problems.  It *probably* isn't safe not to do this,
  1348.      as there is the possibility that FTP servers could die if they
  1349.      try to send it and we're not listening.
  1350.  
  1351.      Testcase for problems (10/30/93): uxc.cso.uiuc.edu,
  1352.      AnswerGarden COPYRIGHT in X11R5 contrib clients.
  1353.  
  1354.      Of course, we may already be triggering hostile actions
  1355.      by allowing client-side interrupts as follows... */
  1356.       if (rv != HT_INTERRUPTED)
  1357.     {
  1358.       if (TRACE)
  1359.         fprintf (stderr, "FTP: Picking up final reply...\n");
  1360. #ifdef OLD
  1361.       status = response (NIL);        /* Pick up final reply */
  1362.       if (status == HT_INTERRUPTED)
  1363.         {
  1364.           if (TRACE)
  1365.         fprintf (stderr, "FTP: Interrupted picking up final reply.\n");
  1366.           HTProgress ("Connection interrupted.");
  1367.  
  1368.           NETCLOSE (control);
  1369.           control = -1;
  1370.           close_master_socket ();
  1371.  
  1372.           (*targetClass.handle_interrupt)(stream);
  1373.  
  1374.           return HT_INTERRUPTED;
  1375.         }
  1376.       if (status != 2)
  1377.         {
  1378.           NETCLOSE (control);
  1379.           control = -1;
  1380.           close_master_socket ();
  1381.           return HT_NOT_LOADED;
  1382.         }
  1383. #else
  1384.       if (TRACE)
  1385.         fprintf (stderr, "FTP: Aw hell, we don't care about the final reply!\n");
  1386. #endif
  1387.     }
  1388.  
  1389.       if (TRACE)
  1390.     fprintf (stderr, "FTP: Closing control socket %d\n", control);
  1391.       NETCLOSE(control);
  1392.       control = -1;
  1393.       close_master_socket ();
  1394.  
  1395.       if (rv != HT_INTERRUPTED)
  1396.     {
  1397. #else
  1398.     return -1;
  1399. #endif
  1400.       /* WAIT until all sockets have been closed. */
  1401.       if (TRACE)
  1402.         fprintf (stderr, "FTP: Calling free method, finally.\n");
  1403.       (*targetClass.free)(stream);
  1404.     }
  1405.  
  1406.       return rv == HT_INTERRUPTED ? HT_INTERRUPTED : HT_LOADED;
  1407.     }
  1408. } /* open_file_read */
  1409.